# ELFS是待构建的产物列表，在这里添加新程序名。扩展名用elf。
# 构建（make）时会找对应的源文件（.cpp）编译成可执行文件（.elf）。
# 首次是":="表示变量赋值，后续要用"+="表示追加。
# 如有多次":="（我的一位朋友粗心地拷贝了首行），之前的值会丢失，表现为不再编译某些文件。
CPPS := $(sort $(wildcard src/*.cpp))
ELFS := $(patsubst src/%.cpp,out/%.elf,$(CPPS))
TSTS := $(patsubst src/%.cpp,test_%,$(CPPS))

# 列举符合命名模式的作业目录：^t\dp\d{2}_\w+$
TASKS := $(sort $(wildcard t[0-9]p[0-9][0-9]_*/))
TASK1 := $(sort $(wildcard     t1p[0-9][0-9]_*/))
TASK2 := $(sort $(wildcard     t2p[0-9][0-9]_*/))
TASK3 := $(sort $(wildcard     t3p[0-9][0-9]_*/))
TASK4 := $(sort $(wildcard     t4p[0-9][0-9]_*/))

# 编译器、链接器
CC  := cc
CXX := c++
LD  := ld.gold

# 定义DEBUG环境变量时，添加调试信息，不优化。
ifeq ($(DEBUG),1)
	OFLAGS 	:= -g -pg
else
	OFLAGS 	:= -O2
endif

# 编译选项：2级优化、全部常规警告、额外警告、非标警告、变量屏蔽警告
BFLAGS   	:= $(OFLAGS) -Wall -Wextra -Wpedantic -Wshadow
# C17/C++17规范
CFLAGS 	 	:= -std=c17 $(BFLAGS)
CXXFLAGS 	:= -std=c++17 $(BFLAGS)

# 链接选项：数学库
LDFLAGS  	:= -lm

ifeq ($(CLANG),12)
CC  			:= clang-12
CXX 			:= clang++-12
endif

ifeq ($(CLANG),14)
CC  			:= clang-14
CXX 			:= clang++-14
endif

# 简化编译输出的信息，隐藏编译命令行。
ifneq ($(VERBOSE),1)
QCC 	= @echo '  cc    '$<;
QCXX	= @echo '  c++   '$<;
QTEST	= @echo '  test  '$<;
endif

# 第一条规则是“终极目标”
all: $(ELFS)
	for dir in $(TASKS); do \
		$(MAKE) -C $$dir $@; \
	done

.PHONY: all clean cleanout rebuild retest rt1 rt2 rt3 rt4 test t1 t2 t3 t4

# 自动推导规则：程序（elf）依赖于源码（cpp）
# $^代表源（xxx.cpp），$@代表输出（xxx.elf）
# 构建的命令行开头必须是一个TAB符，不能用空格！
out/%.elf: src/%.c
	$(QCC) $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

out/%.elf: src/%.cpp
	$(QCXX) $(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@

# 测试依赖：程序、输入、答案
#   动作：%.in重定向到程序作为输入、程序输出重定向到%.out、对比答案和输出
test_%: out/%.elf in/%.in answers/%.ans
	$(QTEST) ./out/$*.elf < in/$*.in > out/$*.out && diff answers/$*.ans out/$*.out

# 清理全部产物
clean:
	@rm -f out/*.out out/*.elf
	for dir in $(TASKS); do \
		$(MAKE) -C $$dir $@; 	\
	done

# 清理输出文件
cleanout:
	@rm -f out/*.out
	for dir in $(TASKS); do \
		$(MAKE) -C $$dir $@; 	\
	done

data:
	for dir in $(TASKS); do \
		$(MAKE) -C $$dir $@; 	\
	done

# 重建全部产物（就是先清理、再全部构建）
rebuild: clean all
	for dir in $(TASKS); do \
		$(MAKE) -C $$dir $@; 	\
	done

retest:
	for dir in $(TASKS); do \
		$(MAKE) -C $$dir $@; 	\
	done

rt1:
	for dir in $(TASK1); do $(MAKE) -C $$dir retest; done
rt2:
	for dir in $(TASK2); do $(MAKE) -C $$dir retest; done
rt3:
	for dir in $(TASK3); do $(MAKE) -C $$dir retest; done
rt4:
	for dir in $(TASK4); do $(MAKE) -C $$dir retest; done

# 测试
test: all $(TSTS)
	for dir in $(TASKS); do \
		$(MAKE) -C $$dir $@; 	\
	done

t1:
	for dir in $(TASK1); do $(MAKE) -C $$dir test; done
t2:
	for dir in $(TASK2); do $(MAKE) -C $$dir test; done
t3:
	for dir in $(TASK3); do $(MAKE) -C $$dir test; done
t4:
	for dir in $(TASK4); do $(MAKE) -C $$dir test; done

retest: cleanout test
